package org.jsola.hr.service.impl;

import com.alibaba.excel.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import io.micrometer.core.instrument.util.StringUtils;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.BigDecimalKit;
import org.jsola.hr.common.StreamKit;
import org.jsola.hr.dto.EmployeeSalaryDTO;
import org.jsola.hr.dto.HrCustomerDetailsAddDTO;
import org.jsola.hr.entity.GroupCompanyDO;
import org.jsola.hr.provider.ISalaryProviderService;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.EmpSocInfoVO;
import org.jsola.hr.vo.UserCompanyVO;
import org.jsola.salary.constant.SalaryConstants;
import org.jsola.salary.dto.CustomerDetailsAddDTO;
import org.jsola.salary.dto.CustomerDetailsUpdateDTO;
import org.jsola.salary.dto.CustomerSettlementAddDTO;
import org.jsola.salary.dto.EmployeeDetailsAddDTO;
import org.jsola.salary.entity.CustomerDetailsDO;
import org.jsola.salary.entity.EmployeeDetailsDO;
import org.jsola.salary.entity.EmployeeSalaryDO;
import org.jsola.salary.entity.SalaryGroupSalaryDO;
import org.jsola.salary.vo.CustomerDetailsVO;
import org.jsola.salary.vo.SalaryGroupVO;
import org.jsola.salary.vo.SalaryTemplateItemVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.HtmlUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.salary.constant.CustomerDetailsType.CUSTOMER;
import static org.jsola.salary.constant.CustomerDetailsType.SALARY;
import static org.jsola.salary.constant.CustomerDetailsType.SOCIAL;

/**
 * 子公司分管公司关系表
 *
 * @author Mr.Zheng
 */
@Service("hrHrCustomerDetailsServiceImpl")
@Slf4j
public class HrCustomerDetailsServiceImpl implements IHrCustomerDetailsService {


    @Autowired
    private ISalaryProviderService salaryProviderService;
    @Autowired
    private IEmpSocInfoService empSocInfoService;
    @Autowired
    private IChildCompanyService childCompanyService;
    @Autowired
    private ICompanyService companyService;
    @Autowired
    private IGroupCompanyService groupCompanyService;

    @Override
    @Transactional(value = SalaryConstants.TX, rollbackFor = Exception.class)
    public CustomerDetailsDO save(CustomerDetailsAddDTO customerDetailsAddDTO, TokenUser tokenUser) {
        Integer yearly = customerDetailsAddDTO.getYearly();
        Integer monthly = customerDetailsAddDTO.getMonthly();
        String companyId = customerDetailsAddDTO.getCompanyId();
        // 已创建的客户明细
        List<CustomerDetailsDO> customerDetailsDOList = salaryProviderService
                .selectCustomerDetailsByMonthlyCompanyIdAndType(yearly
                        , monthly, companyId, customerDetailsAddDTO.getType(), tokenUser);
        // 删除已创建的 重新创建
        if (!CollectionUtils.isEmpty(customerDetailsDOList)) {
            salaryProviderService.deleteCustomerDetailsByIds(tokenUser
                    , customerDetailsDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        // 类型校验 表头
        String type = customerDetailsAddDTO.getType();
        Integer salaryYearly = customerDetailsAddDTO.getSalaryYearly();
        Integer salaryMonthly = customerDetailsAddDTO.getSalaryMonthly();
        Integer socialYearly = customerDetailsAddDTO.getSocialYearly();
        Integer socialMonthly = customerDetailsAddDTO.getSocialMonthly();
        // 构建表头
        customerDetailsAddDTO.setHeader(createHeader(type, companyId
                , salaryYearly, salaryMonthly, socialYearly, socialMonthly, tokenUser));
        // 个人所属期
        customerDetailsAddDTO.setPersonalPeriod(yearly + "年" + monthly + "月");
        // 保存
        CustomerDetailsDO customerDetailsDO = salaryProviderService.saveCustomerDetails(customerDetailsAddDTO, tokenUser);
        // 创建员工明细
        List<EmployeeDetailsAddDTO> employeeDetailsAddDTOList = new ArrayList<>();
        // 服务费(一次性)
        BigDecimal shouldAmount = BigDecimal.ZERO;
        if (SALARY.getValue().equals(type)) {
            // 月工资
            EmployeeSalaryDTO employeeSalary = getEmployeeSalary(yearly, monthly, salaryYearly, salaryMonthly
                    , companyId, customerDetailsDO.getId(), tokenUser);
            employeeDetailsAddDTOList = employeeSalary.getEmployeeDetailsAddDTOList();
            shouldAmount = BigDecimalKit.nullToZero(employeeSalary.getShouldAmount());
        } else if (SOCIAL.getValue().equals(type)) {
            employeeDetailsAddDTOList = getEmpSoc(customerDetailsAddDTO.getYearly(), customerDetailsAddDTO.getMonthly(),
                    customerDetailsAddDTO.getSocialYearly(), socialMonthly, companyId
                    , customerDetailsDO.getId(), customerDetailsDO.getHeader(), tokenUser);
        } else if (CUSTOMER.getValue().equals(type)) {
            // 月工资
            EmployeeSalaryDTO employeeSalary = getEmployeeSalary(yearly, monthly, salaryYearly
                    , salaryMonthly, companyId, customerDetailsDO.getId(), tokenUser);
            List<EmployeeDetailsAddDTO> employeeSalaryList = employeeSalary.getEmployeeDetailsAddDTOList();
            shouldAmount = BigDecimalKit.nullToZero(employeeSalary.getShouldAmount());
            // 薪资人员-信息
            Map<String, EmployeeDetailsAddDTO> salaryMap = employeeSalaryList.parallelStream()
                    .collect(Collectors.toMap(EmployeeDetailsAddDTO::getEmpId, v -> v, (v1, v2) -> v1));
            // 薪资人员
            List<String> salaryEmpIds = employeeSalaryList.parallelStream()
                    .map(EmployeeDetailsAddDTO::getEmpId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(salaryEmpIds)) {
                salaryEmpIds = new ArrayList<>();
            }
            // 社保
            List<EmployeeDetailsAddDTO> empSocList = getEmpSoc(customerDetailsAddDTO.getYearly()
                    , customerDetailsAddDTO.getMonthly(), customerDetailsAddDTO.getSocialYearly()
                    , socialMonthly, companyId, customerDetailsDO.getId(), customerDetailsDO.getHeader(), tokenUser);
            // 社保人员-信息
            Map<String, EmployeeDetailsAddDTO> empSocMap = empSocList.parallelStream()
                    .collect(Collectors.toMap(EmployeeDetailsAddDTO::getEmpId, v -> v, (v1, v2) -> v1));
            // 社保人员
            List<String> socEmpIds = empSocList.parallelStream()
                    .map(EmployeeDetailsAddDTO::getEmpId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(socEmpIds)) {
                socEmpIds = new ArrayList<>();
            }
            salaryEmpIds.addAll(socEmpIds);
            // 去重
            salaryEmpIds = salaryEmpIds.parallelStream().distinct().collect(Collectors.toList());
            for (String empId : salaryEmpIds) {
                EmployeeDetailsAddDTO employeeDetailsAddDTO = new EmployeeDetailsAddDTO();
                EmployeeDetailsAddDTO salaryDTO = salaryMap.get(empId);
                // 实发
                BigDecimal paidWages = BigDecimal.ZERO;
                // 个人社保
                BigDecimal empSocialSecurity = BigDecimal.ZERO;
                // 个人公积金
                BigDecimal empProvidentFund = BigDecimal.ZERO;
                // 个人所得税
                BigDecimal tax = BigDecimal.ZERO;
                // 企业社保
                BigDecimal enterpriseSocialSecurity = BigDecimal.ZERO;
                // 企业公积金
                BigDecimal enterpriseProvidentFund = BigDecimal.ZERO;
                // 服务费
                BigDecimal serviceCharge = BigDecimal.ZERO;
                // 滞纳金
                BigDecimal fine = BigDecimal.ZERO;
                if (salaryDTO != null) {
                    employeeDetailsAddDTO = salaryDTO.to(EmployeeDetailsAddDTO.class);
                    // 薪资取值：实发工资 + 个人社保公积金扣除 + 个税扣除 + 薪资服务费
                    // 薪资服务费
                    // 实发工资
                    paidWages = BigDecimalKit.nullToZero(salaryDTO.getPaidWages());
                    // 个人社保
                    empSocialSecurity = BigDecimalKit.nullToZero(salaryDTO.getEmpSocialSecurity());
                    // 个人公积金
                    empProvidentFund = BigDecimalKit.nullToZero(salaryDTO.getEmpProvidentFund());
                    // 个人所得税
                    tax = BigDecimalKit.nullToZero(salaryDTO.getTax());
                    // 服务费
                    serviceCharge = BigDecimalKit.nullToZero(salaryDTO.getServiceCharge());
                }
                EmployeeDetailsAddDTO socDTO = empSocMap.get(empId);
                if (socDTO != null) {
                    if (salaryDTO == null) {
                        employeeDetailsAddDTO = socDTO.to(EmployeeDetailsAddDTO.class);
                    }
                    // 参保取值：
                    // 员工参保状态
                    employeeDetailsAddDTO.setEmpSocStatus(socDTO.getEmpSocStatus());
                    // 参保城市
                    employeeDetailsAddDTO.setSocCity(socDTO.getSocCity());
                    // 参保城市名称
                    employeeDetailsAddDTO.setSocCityName(socDTO.getSocCityName());
                    if (salaryDTO == null) {
                        // 个人社保
                        empSocialSecurity = BigDecimalKit.nullToZero(socDTO.getEmpSocialSecurity());
                        // 个人公积金
                        empProvidentFund = BigDecimalKit.nullToZero(socDTO.getEmpProvidentFund());
                    }
                    // 企业社保
                    enterpriseSocialSecurity = BigDecimalKit.nullToZero(socDTO.getEnterpriseSocialSecurity());
                    // 企业公积金
                    enterpriseProvidentFund = BigDecimalKit.nullToZero(socDTO.getEnterpriseProvidentFund());
                    if (salaryDTO == null) {
                        // 社保服务费
                        serviceCharge = BigDecimalKit.nullToZero(socDTO.getServiceCharge());
                    } else {
                        // 存在薪资 服务费 = 薪资服务费 + 社保服务费
                        serviceCharge = serviceCharge.add(BigDecimalKit.nullToZero(socDTO.getServiceCharge()));
                    }
                    // 滞纳金
                    fine = BigDecimalKit.nullToZero(socDTO.getFine());
                }
                employeeDetailsAddDTO.setCustomerDetailsId(customerDetailsDO.getId());
                employeeDetailsAddDTO.setYearly(customerDetailsAddDTO.getYearly());
                employeeDetailsAddDTO.setMonthly(customerDetailsAddDTO.getMonthly());
                employeeDetailsAddDTO.setCompanyId(companyId);
                employeeDetailsAddDTO.setEmpId(empId);
                // 实发工资
                employeeDetailsAddDTO.setPaidWages(paidWages);
                // 个人社保
                employeeDetailsAddDTO.setEmpSocialSecurity(empSocialSecurity);
                // 个人公积金
                employeeDetailsAddDTO.setEmpProvidentFund(empProvidentFund);
                // 个人所得税
                employeeDetailsAddDTO.setTax(tax);
                // 服务费
                employeeDetailsAddDTO.setServiceCharge(serviceCharge);
                // 企业社保
                employeeDetailsAddDTO.setEnterpriseSocialSecurity(enterpriseSocialSecurity);
                // 企业公积金
                employeeDetailsAddDTO.setEnterpriseProvidentFund(enterpriseProvidentFund);
                // 企业公积金
                employeeDetailsAddDTO.setFine(fine);
                // 客户费用 = 实发工资 + 个人社保公积金扣除 + 个税扣除 + 服务费(薪资 + 社保) + 企业社保 + 企业公积金 + 滞纳金
                BigDecimal customerFees = paidWages.add(empSocialSecurity).add(empProvidentFund).add(tax)
                        .add(serviceCharge).add(enterpriseSocialSecurity).add(enterpriseProvidentFund).add(fine);
                employeeDetailsAddDTO.setCustomerFees(customerFees);
                employeeDetailsAddDTOList.add(employeeDetailsAddDTO);
            }
        }
        // 保存员工明细 并将统计数据放入客户明细
        CustomerDetailsUpdateDTO customerDetailsUpdateDTO = customerDetailsDO.to(CustomerDetailsUpdateDTO.class);
        if (CollectionUtils.isEmpty(employeeDetailsAddDTOList)) {
            // 没创建月工资的公司 置空
            customerDetailsUpdateDTO.setCustomerFees(BigDecimal.ZERO);
            customerDetailsUpdateDTO.setEmpNumber(0);
            customerDetailsUpdateDTO.setEnterpriseProvidentFund(BigDecimal.ZERO);
            customerDetailsUpdateDTO.setEnterpriseSocialSecurity(BigDecimal.ZERO);
            customerDetailsUpdateDTO.setPaidWages(BigDecimal.ZERO);
            customerDetailsUpdateDTO.setServiceCharge(shouldAmount);
            customerDetailsUpdateDTO.setShouldWages(BigDecimal.ZERO);
        } else {
            List<EmployeeDetailsDO> employeeDetailsDOList =
                    salaryProviderService.batchSaveEmployeeDetails(employeeDetailsAddDTOList, tokenUser);
            // 应发
            BigDecimal shouldWages = BigDecimal.ZERO;
            // 实发
            BigDecimal paidWages = BigDecimal.ZERO;
            // 企业公积金
            BigDecimal enterpriseProvidentFund = BigDecimal.ZERO;
            // 公司社保
            BigDecimal enterpriseSocialSecurity = BigDecimal.ZERO;
            // 服务费
            BigDecimal serviceCharge = BigDecimal.ZERO;
            // 滞纳金
            BigDecimal fine = BigDecimal.ZERO;
            // 客户费用
            BigDecimal customerFees = BigDecimal.ZERO;
            if (!CollectionUtils.isEmpty(employeeDetailsAddDTOList)) {
                for (EmployeeDetailsDO employeeDetailsDO : employeeDetailsDOList) {
                    shouldWages = shouldWages.add(BigDecimalKit.nullToZero(employeeDetailsDO.getShouldWages()));
                    paidWages = paidWages.add(BigDecimalKit.nullToZero(employeeDetailsDO.getPaidWages()));
                    enterpriseProvidentFund = enterpriseProvidentFund.add(BigDecimalKit.nullToZero(employeeDetailsDO.getEnterpriseProvidentFund()));
                    enterpriseSocialSecurity = enterpriseSocialSecurity.add(BigDecimalKit.nullToZero(employeeDetailsDO.getEnterpriseSocialSecurity()));
                    serviceCharge = serviceCharge.add(BigDecimalKit.nullToZero(employeeDetailsDO.getServiceCharge()));
                    fine = fine.add(BigDecimalKit.nullToZero(employeeDetailsDO.getFine()));
                    customerFees = customerFees.add(BigDecimalKit.nullToZero(employeeDetailsDO.getCustomerFees()));
                }
            }
            if (SALARY.getValue().equals(type)) {
                // 薪资明细：本月计薪人数 应发工资 实发工资 服务费 客户费用
                customerDetailsUpdateDTO.setShouldWages(shouldWages);
                customerDetailsUpdateDTO.setPaidWages(paidWages);
            } else if (SOCIAL.getValue().equals(type)) {
                // 社保明细：本月参保人数 企业社保 企业公积金 服务费 滞纳金 客户费用
                customerDetailsUpdateDTO.setEnterpriseProvidentFund(enterpriseProvidentFund);
                customerDetailsUpdateDTO.setEnterpriseSocialSecurity(enterpriseSocialSecurity);
                customerDetailsUpdateDTO.setFine(fine);
            } else if (CUSTOMER.getValue().equals(type)) {
                // 人力成本 = 应发工资 + 企业公积金 + 企业社保 + 滞纳金
                // 人力成本 = 应发工资 + 免税工资 + 企业公积金 + 企业社保 + 滞纳金(需要+免税工资)
                BigDecimal laborCost = shouldWages.add(enterpriseProvidentFund).add(enterpriseSocialSecurity).add(fine);
                customerDetailsUpdateDTO.setLaborCost(laborCost);
                // 客户明细：本月人数 应发工资 实发工资 企业社保 企业公积金 服务费 滞纳金 客户费用
                customerDetailsUpdateDTO.setShouldWages(shouldWages);
                customerDetailsUpdateDTO.setPaidWages(paidWages);
                customerDetailsUpdateDTO.setEnterpriseProvidentFund(enterpriseProvidentFund);
                customerDetailsUpdateDTO.setEnterpriseSocialSecurity(enterpriseSocialSecurity);
                customerDetailsUpdateDTO.setFine(fine);
            }
            // 滞纳金
            if (!SALARY.getValue().equals(type)) {
                boolean hadFine = employeeDetailsAddDTOList.parallelStream().anyMatch(employeeDetailsAddDTO ->
                        employeeDetailsAddDTO.getFine() != null && BigDecimal.ZERO.compareTo(employeeDetailsAddDTO.getFine()) != 0);
                if (hadFine) {
                    customerDetailsUpdateDTO.setHeader(customerDetailsUpdateDTO.getHeader() + ",滞纳金");
                }
            }
            // 本月计薪人数
            customerDetailsUpdateDTO.setEmpNumber(employeeDetailsAddDTOList.size());
            // 服务费
            customerDetailsUpdateDTO.setServiceCharge(serviceCharge.add(shouldAmount));
            // 客户费用
            customerDetailsUpdateDTO.setCustomerFees(customerFees.add(shouldAmount));
        }
        salaryProviderService.updateCustomerDetails(customerDetailsUpdateDTO, tokenUser);
        return customerDetailsUpdateDTO.to(CustomerDetailsDO.class);
    }

    /**
     * 构建表头
     *
     * @param type          类型
     * @param companyId     用工单位Id
     * @param salaryYearly  薪资来源年
     * @param salaryMonthly 薪资来源月
     * @param socialYearly  社保来源年
     * @param socialMonthly 社保来源月
     * @param tokenUser     登录用户
     * @return 表头
     */
    private String createHeader(String type, String companyId, Integer salaryYearly, Integer salaryMonthly
            , Integer socialYearly, Integer socialMonthly, TokenUser tokenUser) {
        String header;
        if (SALARY.getValue().equals(type)) {
            ParamException.isFalse(salaryMonthly == null
                    || salaryYearly == null, "薪资数据来源不能为空");
            // 自定义工资项
            List<SalaryTemplateItemVO> salaryTemplateItemVOList = childCompanyService
                    .selectUsedTemplateByCompanyList(salaryYearly
                            , salaryMonthly, "company", companyId, tokenUser);
            String names = salaryTemplateItemVOList.parallelStream()
                    .map(SalaryTemplateItemVO::getName).collect(Collectors.joining(","));
            if (StringUtils.isNotEmpty(names)) {
                names += ",";
            }
            header = "姓名,身份证号," + names + "应发工资,个人社保,个人公积金,个人所得税,实发工资,服务费";
        } else if (SOCIAL.getValue().equals(type)) {
            ParamException.isFalse(socialYearly == null
                    || socialMonthly == null, "社保数据来源不能为空");
            // 滞纳金有值再放入表头
            header = "姓名,身份证号,参保城市,企业社保,企业公积金,服务费";
        } else if (CUSTOMER.getValue().equals(type)) {
            ParamException.isFalse(salaryMonthly == null || salaryYearly == null, "薪资数据来源不能为空");
            ParamException.isFalse(socialYearly == null || socialMonthly == null, "社保数据来源不能为空");
            // 自定义工资项
            List<SalaryTemplateItemVO> salaryTemplateItemVOList = childCompanyService
                    .selectUsedTemplateByCompanyList(salaryYearly
                            , salaryMonthly, "company", companyId, tokenUser);
            String names = salaryTemplateItemVOList.parallelStream()
                    .map(SalaryTemplateItemVO::getName).collect(Collectors.joining(","));
            // 滞纳金有值再放入表头
            if (StringUtils.isNotEmpty(names)) {
                names += ",";
            }
            header = "姓名,身份证号," + names + "应发工资,个人社保,个人公积金,个人所得税,实发工资,企业社保,企业公积金,服务费";
        } else {
            throw new ParamException("类型选择有误，请刷新后重试");
        }
        return header;
    }


    /**
     * 构造月工资的员工保存对象
     *
     * @param yearly            客户明细年
     * @param monthly           客户明细月
     * @param salaryYearly      月工资年
     * @param salaryMonthly     月工资月
     * @param companyId         用工单位Id
     * @param customerDetailsId 客户明细Id
     * @param tokenUser         登录用户
     * @return 创建月工资的员工
     */
    private EmployeeSalaryDTO getEmployeeSalary(Integer yearly, Integer monthly, Integer salaryYearly
            , Integer salaryMonthly, String companyId, Long customerDetailsId, TokenUser tokenUser) {
        EmployeeSalaryDTO employeeSalaryDTO = new EmployeeSalaryDTO();
        // 服务费(一次性)
        BigDecimal shouldAmount = BigDecimal.ZERO;
        // 用工单位下的薪资组
        List<SalaryGroupVO> salaryGroupVOList = salaryProviderService.selectSalaryGroupByCompanyId(companyId);
        List<EmployeeSalaryDO> employeeSalaryDOList = salaryProviderService.selectByCompanyIdsAndMonthly(
                Collections.singletonList(companyId), salaryYearly, salaryMonthly, tokenUser);
        // 薪资组Id-VO
        Map<Long, SalaryGroupVO> salaryGroupIdVoMap = new HashMap<>(16);
        // 已创建的薪资组月工资
        List<SalaryGroupSalaryDO> salaryGroupSalaryDOList
                = salaryProviderService.selectCreatedSalaryGroupSalary(salaryYearly, salaryMonthly, companyId);
        // 已创建月工资表的薪资组
        List<Long> hadSalaryGroupIds = salaryGroupSalaryDOList
                .parallelStream().map(SalaryGroupSalaryDO::getSalaryGroupId).collect(Collectors.toList());
        // 默认薪资组
        SalaryGroupVO allEmpGroup = null;
        for (SalaryGroupVO salaryGroupVO : salaryGroupVOList) {
            // 若薪资组一次性费用收取当月未创建月工资 则不收取一次性费用
            if (salaryGroupVO.getServiceFeeType() == 3 && hadSalaryGroupIds.contains(salaryGroupVO.getId())) {
                // 一次性费用 且是当月收费 需要统计到公司的客户明细中
                SimpleDateFormat format = new SimpleDateFormat("yyyyMM");
                BigDecimal chargeAmount = salaryGroupVO.getChargeAmount();
                // 客户明细
                String aa;
                if (monthly < 10) {
                    aa = "" + yearly + "0" + monthly;
                } else{
                    aa = "" + yearly + monthly;
                }
                if (aa.equals(format.format(salaryGroupVO.getBillingMonth()))
                        && chargeAmount != null) {
                    shouldAmount = shouldAmount.add(chargeAmount);
                }
            }
            // 组建成Map
            salaryGroupIdVoMap.put(salaryGroupVO.getId(), salaryGroupVO);
            // 取出全员薪资组
            if (salaryGroupVO.getIsAllEmp() != null && salaryGroupVO.getIsAllEmp()) {
                allEmpGroup = salaryGroupVO;
            }
        }
        if (CollectionUtils.isEmpty(employeeSalaryDOList)) {
            employeeSalaryDTO.setEmployeeDetailsAddDTOList(new ArrayList<>());
            employeeSalaryDTO.setShouldAmount(shouldAmount);
            employeeSalaryDTO.setShouldAmount(shouldAmount);
            return employeeSalaryDTO;
        }
        SalaryGroupVO finalAllEmpGroup = allEmpGroup;
        List<EmployeeDetailsAddDTO> employeeDetailsAddDTOList = employeeSalaryDOList.parallelStream().map(employeeSalaryDO -> {
            EmployeeDetailsAddDTO employeeDetailsAddDTO = employeeSalaryDO.to(EmployeeDetailsAddDTO.class);
            // 服务费根据薪资组计算而来
            SalaryGroupVO salaryGroupVO = salaryGroupIdVoMap.get(employeeDetailsAddDTO.getSalaryGroupId());
            // 若员工所在薪资组为空 认为在全员薪资组中
            if (salaryGroupVO == null) {
                salaryGroupVO = finalAllEmpGroup;
            }
            if (salaryGroupVO == null) {
                // 薪资组被删除 同时还没有默认薪资组(理论上不会出现)
                log.error("薪资组被删除 同时还没有默认薪资组,companyId:{}", companyId);
                employeeSalaryDO.setServiceCharge(BigDecimal.ZERO);
            } else {
                switch (salaryGroupVO.getServiceFeeType()) {
                    case 1:
                        // 按人头计算
                        employeeSalaryDO.setServiceCharge(salaryGroupVO.getChargeAmount());
                        break;
                    case 2:
                        // 百分比
                        BigDecimal proportion = salaryGroupVO.getProportion()
                                .divide(new BigDecimal("100"), RoundingMode.HALF_EVEN);
                        // 按工资统计项计算
                        switch (salaryGroupVO.getServiceFeeItem()) {
                            case "应发工资":
                                // 收费比例
                                ParamException.notNull(salaryGroupVO.getProportion(), "应发工资百分比不能为空");
                                employeeSalaryDO.setServiceCharge(BigDecimalKit
                                        .nullToZero(employeeSalaryDO.getShouldWages()).multiply(proportion));
                                break;
                            case "人力成本":
                                // 收费比例
                                ParamException.notNull(salaryGroupVO.getProportion(), "人力成本百分比不能为空");
                                // 薪资的人力成本 = 应发工资 + 薪资表的社保公积金
                                BigDecimal laborCost = BigDecimalKit.nullToZero(employeeSalaryDO.getShouldWages())
                                        .add(BigDecimalKit.nullToZero(employeeSalaryDO.getEnterpriseSocialSecurity()))
                                        .add(BigDecimalKit.nullToZero(employeeSalaryDO.getEnterpriseProvidentFund()));
                                employeeSalaryDO.setServiceCharge(laborCost.multiply(proportion));
                                break;
                            default:
                                employeeSalaryDO.setServiceCharge(BigDecimal.ZERO);
                        }
                        break;
                    case 3:
                        // 一次性费用 每人的金额是0
                    default:
                        // 错误数据 金额为0
                        employeeSalaryDO.setServiceCharge(BigDecimal.ZERO);
                        break;
                }
            }
//            // 免税工资
//            BigDecimal dutyFree = BigDecimal.ZERO;
//            if (StringUtils.isEmpty(employeeSalaryDO.getItemDetail())) {
//                List<SalaryTemplateItemVO> salaryTemplateItemVOList = JSONArray
//                        .parseArray(employeeSalaryDO.getItemDetail(), SalaryTemplateItemVO.class);
//                for (SalaryTemplateItemVO salaryTemplateItemVO : salaryTemplateItemVOList) {
//                    // 未使用项跳过
//                    if (!salaryTemplateItemVO.getIsUse()) {
//                        continue;
//                    }
//                    // 非增加项或减少项 跳过
//                    if (salaryTemplateItemVO.getIsAdd() == null || (salaryTemplateItemVO.getIsAdd() != 1
//                            && salaryTemplateItemVO.getIsAdd() != 2)) {
//                        continue;
//                    }
//                    // 免税且计算收入先取出 算完税再放入
//                    if (salaryTemplateItemVO.getIsTaxCalculation() != null && !salaryTemplateItemVO.getIsTaxCalculation()) {
//                        if (salaryTemplateItemVO.getIsAdd() == 1) {
//                            dutyFree = dutyFree.add(BigDecimalKit.nullToZero(salaryTemplateItemVO.getAmount()));
//                        } else if (salaryTemplateItemVO.getIsAdd() == 2) {
//                            dutyFree = dutyFree.subtract(BigDecimalKit.nullToZero(salaryTemplateItemVO.getAmount()));
//                        }
//                    }
//                }
//            }
            // 旧逻辑
            // 薪资客户费用 = 应发工资 + 免税工资 + 薪资服务费
            // 应发工资 = 实发工资 + 个人社保公积金扣除 + 个税扣除 - 免税工资
            // 新逻辑 以上公式合并
            // 薪资客户费用 = 实发工资 + 个人社保公积金扣除 + 个税扣除 + 薪资服务费
            BigDecimal customerFees = BigDecimalKit.nullToZero(employeeSalaryDO.getPaidWages())
                    .add(BigDecimalKit.nullToZero(employeeSalaryDO.getEmpSocialSecurity()))
                    .add(BigDecimalKit.nullToZero(employeeSalaryDO.getEmpProvidentFund()))
                    .add(BigDecimalKit.nullToZero(employeeSalaryDO.getTax()))
                    .add(employeeSalaryDO.getServiceCharge());
            employeeDetailsAddDTO.setServiceCharge(employeeSalaryDO.getServiceCharge());
            employeeDetailsAddDTO.setCustomerDetailsId(customerDetailsId);
            employeeDetailsAddDTO.setCustomerFees(customerFees);
            return employeeDetailsAddDTO;
        }).collect(Collectors.toList());
        employeeSalaryDTO.setEmployeeDetailsAddDTOList(employeeDetailsAddDTOList);
        employeeSalaryDTO.setShouldAmount(shouldAmount);
        return employeeSalaryDTO;
    }

    /**
     * 构造社保的员工保存对象
     *
     * @param yearly            年
     * @param monthly           月
     * @param socialYearly      社保年
     * @param socialMonthly     社保月
     * @param companyId         用工单位Id
     * @param customerDetailsId 客户明细Id
     * @param header            表头
     * @param tokenUser         登录用户
     * @return 创建社保的员工
     */
    private List<EmployeeDetailsAddDTO> getEmpSoc(Integer yearly, Integer monthly, Integer socialYearly, Integer socialMonthly
            , String companyId, Long customerDetailsId, String header, TokenUser tokenUser) {
        List<EmployeeDetailsAddDTO> employeeDetailsAddDTOList = new ArrayList<>();
        // 社保信息
        List<EmpSocInfoVO> empSocInfoVOList = empSocInfoService
                .selectEmpSocInfoWithCustomerDetails(socialYearly, socialMonthly
                        , Collections.singletonList(companyId), tokenUser);
        if (!CollectionUtils.isEmpty(empSocInfoVOList)) {
            employeeDetailsAddDTOList = empSocInfoVOList.parallelStream().map(empSocInfoVO -> {
                EmployeeDetailsAddDTO employeeDetailsAddDTO = empSocInfoVO.to(EmployeeDetailsAddDTO.class);
                employeeDetailsAddDTO.setYearly(yearly);
                employeeDetailsAddDTO.setMonthly(monthly);
                employeeDetailsAddDTO.setCustomerDetailsId(customerDetailsId);
                employeeDetailsAddDTO.setEmpSocialSecurity(empSocInfoVO.getPersonalSocAmount());
                employeeDetailsAddDTO.setEmpProvidentFund(empSocInfoVO.getPersonalPfAmount());
                employeeDetailsAddDTO.setEnterpriseSocialSecurity(empSocInfoVO.getCompanySocAmount());
                employeeDetailsAddDTO.setEnterpriseProvidentFund(empSocInfoVO.getCompanyPfAmount());
                employeeDetailsAddDTO.setServiceCharge(empSocInfoVO.getServiceCharge());
                // 滞纳金
                BigDecimal accumulationFundFine = empSocInfoVO.getAccumulationFundFine();
                BigDecimal socialSecurityFine = empSocInfoVO.getSocialSecurityFine();
                BigDecimal fine = accumulationFundFine.add(socialSecurityFine);
                employeeDetailsAddDTO.setFine(fine);
                // 社保客户费用 = 企业社保 + 企业公积金 + 社保服务费 + 滞纳金
                BigDecimal customerFees = empSocInfoVO.getCompanyPfAmount().add(empSocInfoVO.getCompanySocAmount())
                        .add(empSocInfoVO.getServiceCharge()).add(fine);
                employeeDetailsAddDTO.setCustomerFees(customerFees);
                return employeeDetailsAddDTO;
            }).collect(Collectors.toList());
        }
        return employeeDetailsAddDTOList;
    }

    @Override
    @Transactional(value = SalaryConstants.TX, rollbackFor = Exception.class)
    public List<CustomerDetailsDO> saveByCompanyIds(HrCustomerDetailsAddDTO hrCustomerDetailsAddDTO, TokenUser tokenUser) {
        List<CustomerDetailsDO> customerDetailsDOList = new ArrayList<>();

        List<HrCustomerDetailsAddDTO.CompanyInfo> companyInfoList = hrCustomerDetailsAddDTO.getCompanyInfoList();
        ParamException.notEmpty(companyInfoList, "未选择创建客户明细的用工单位");
        companyInfoList = companyInfoList.parallelStream()
                .filter(StreamKit.distinctByKey(HrCustomerDetailsAddDTO.CompanyInfo::getCompanyId)).collect(Collectors.toList());
        for (HrCustomerDetailsAddDTO.CompanyInfo companyInfo : companyInfoList) {
            CustomerDetailsAddDTO customerDetailsAddDTO = hrCustomerDetailsAddDTO.to(CustomerDetailsAddDTO.class);
            customerDetailsAddDTO.setCompanyId(companyInfo.getCompanyId());
            String companyName = companyInfo.getCompanyName();
            companyName = companyName.replaceAll("&nbsp;", " ");
            customerDetailsAddDTO.setName(HtmlUtils.htmlUnescape(companyName));
            CustomerDetailsDO customerDetailsDO = save(customerDetailsAddDTO, tokenUser);
            customerDetailsDOList.add(customerDetailsDO);
        }
        // 生成客户明细表 保存同步生成客户结算
        if (CUSTOMER.getValue().equals(hrCustomerDetailsAddDTO.getType())) {
            List<CustomerSettlementAddDTO> customerSettlementAddDTOList = customerDetailsDOList.parallelStream()
                    .map(customerDetailsDO -> {
                        // 应到账金额 = 客户费用
                        BigDecimal totalAmount = BigDecimalKit.nullToZero(customerDetailsDO.getCustomerFees());
                        if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
                            return null;
                        }
                        CustomerSettlementAddDTO customerSettlementAddDTO = new CustomerSettlementAddDTO();
                        customerSettlementAddDTO.setCompanyId(customerDetailsDO.getCompanyId());
                        customerSettlementAddDTO.setCompanyName(customerDetailsDO.getName());
                        customerSettlementAddDTO.setLaborCost(customerDetailsDO.getLaborCost());
                        customerSettlementAddDTO.setMonthly(customerDetailsDO.getMonthly());
                        customerSettlementAddDTO.setYearly(customerDetailsDO.getYearly());
                        customerSettlementAddDTO.setServiceFeeAmount(customerDetailsDO.getServiceCharge());
                        customerSettlementAddDTO.setTotalAmount(totalAmount);
                        customerSettlementAddDTO.setAmountPaid(BigDecimal.ZERO);
                        customerSettlementAddDTO.setInvoicedAmount(BigDecimal.ZERO);
                        return customerSettlementAddDTO;
                    }).filter(Objects::nonNull).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(customerSettlementAddDTOList)) {
                salaryProviderService.batchSaveCustomerSettlement(customerSettlementAddDTOList, tokenUser);
            }
        }
        return customerDetailsDOList;
    }

    @Override
    public List<CustomerDetailsVO> selectByTypeAndCompanyIds(Integer yearly, Integer monthly, String type
            , List<String> companyIds, TokenUser tokenUser) {
        return salaryProviderService.selectCustomerDetailsVoByTypeAndCompanyIds(yearly, monthly
                , type, companyIds, tokenUser);
    }

    @Override
    public int deleteByIds(TokenUser tokenUser, Long... customerDetailsIds) {
        ParamException.notEmpty(customerDetailsIds, "未选择需要删除的数据");
        int detailsByIds = salaryProviderService.deleteCustomerDetailsByIds(tokenUser, customerDetailsIds);
        List<EmployeeDetailsDO> employeeDetailsDOList = salaryProviderService
                .selectEmployeeDetailsByCustomerDetailsIdList(Arrays.asList(customerDetailsIds), tokenUser);
        // 同时删除员工明细
        salaryProviderService.deleteEmployeeDetailsByIds(tokenUser, employeeDetailsDOList.parallelStream()
                .map(BaseDO::getId).toArray(Long[]::new));

        return detailsByIds;
    }

    @Override
    public CustomerDetailsDO selectLastOne(TokenUser tokenUser) {
        // 登录用户拥有的用工单位权限
        UserCompanyVO userCompanyVO = childCompanyService.selectByUserId(tokenUser.getUserId(), tokenUser.getSiteId());
        CustomerDetailsDO customerDetailsDO;
        if (userCompanyVO.getIsAllChildCompany()) {
            customerDetailsDO = salaryProviderService.selectCustomerDetailsLastOne(null, tokenUser);
        } else {
            List<String> companyIdList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(userCompanyVO.getCompanyIds())) {
                companyIdList.addAll(userCompanyVO.getCompanyIds());
            }
            if (!CollectionUtils.isEmpty(userCompanyVO.getGroupIds())) {
                List<GroupCompanyDO> groupCompanyDOList = groupCompanyService
                        .selectByGroupIds(userCompanyVO.getGroupIds(), tokenUser.getSiteId());
                if (!CollectionUtils.isEmpty(groupCompanyDOList)) {
                    companyIdList.addAll(groupCompanyDOList.parallelStream()
                            .map(GroupCompanyDO::getCompanyId).distinct().collect(Collectors.toList()));
                }
            }
            customerDetailsDO = salaryProviderService.selectCustomerDetailsLastOne(companyIdList, tokenUser);
        }
        return customerDetailsDO;
    }
}




